package com.t2t.top.framework.util;

import org.apache.commons.lang3.StringUtils;

import javax.management.MBeanServer;
import javax.management.MBeanServerFactory;
import javax.management.ObjectName;
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.lang.management.MemoryPoolMXBean;
import java.lang.management.RuntimeMXBean;
import java.net.*;
import java.util.*;

/**
 * 获取系统信息，如：获取JVM内存池、获取本地IP地址、获取JVM进程号等
 * 
 * @author fuhaining
 */
public class SystemInfoUtils {
	
	private static MemoryMXBean memoryMXBean = ManagementFactory.getMemoryMXBean();
	private static RuntimeMXBean runtimeMXBean = ManagementFactory.getRuntimeMXBean();
	
	private static List<String> PATH_RELATED_KEYS = new ArrayList<String>();
	private static List<String> IGNORE_THESE_KEYS = new ArrayList<String>();
	static {
		PATH_RELATED_KEYS.add("sun.boot.class.path");
		PATH_RELATED_KEYS.add("com.ibm.oti.vm.bootstrap.library.path");
		PATH_RELATED_KEYS.add("java.library.path");
		PATH_RELATED_KEYS.add("java.endorsed.dirs");
		PATH_RELATED_KEYS.add("java.ext.dirs");
		PATH_RELATED_KEYS.add("java.class.path");
		
		IGNORE_THESE_KEYS.addAll(PATH_RELATED_KEYS);
		IGNORE_THESE_KEYS.addAll(PATH_RELATED_KEYS);
		IGNORE_THESE_KEYS.add("line.separator");
		IGNORE_THESE_KEYS.add("path.separator");
		IGNORE_THESE_KEYS.add("file.separator");
	}
	
	public static float getJvmVersion()  {
		String property = System.getProperty("java.specification.version");
		try {
			return Float.valueOf(property).floatValue();
		} catch (Exception e) {
			throw new IllegalStateException("Invalid JVM version: '" + property + "'. " + e.getMessage());
		}
	}

	public static long getTotalHeapMemoryInit() {
		return memoryMXBean.getHeapMemoryUsage().getInit();
	}
	public static long getTotalHeapMemoryMax() {
		return memoryMXBean.getHeapMemoryUsage().getMax();
	}
	public static long getTotalHeapMemoryCommitted() {
		return memoryMXBean.getHeapMemoryUsage().getCommitted();
	}
	public static long getTotalHeapMemoryUsed() {
		return memoryMXBean.getHeapMemoryUsage().getUsed();
	}
	public static long getTotalHeapMemoryFree() {
		return getTotalHeapMemoryCommitted() - getTotalHeapMemoryUsed();
	}
	
	public static List<String[]> getMemoryPools() {
		List<String[]> pools = new ArrayList<String[]>();
		List<MemoryPoolMXBean> mxBeans = ManagementFactory.getMemoryPoolMXBeans();
		for (MemoryPoolMXBean mxBean : mxBeans) {
			pools.add(new String[] {mxBean.getName().replaceAll("[ ]", ""), mxBean.getUsage().toString()});
		}
		return pools;
	}
	
	public static Map<String, String> getSystemProperties() {
		Properties props = System.getProperties();
		Map<String, String> properties = new TreeMap(props);
		properties.keySet().removeAll(IGNORE_THESE_KEYS);
		
		for (String key : properties.keySet()) {
			if (key.indexOf("password") != -1) {
				properties.put(key, "********");
			}
		}
		
		return properties;
	}
	
	public static String getLocalAddress() {
		try {
			// 遍历网卡，查找一个非回路ip地址并返回
			Enumeration<NetworkInterface> enumeration = NetworkInterface.getNetworkInterfaces();
			ArrayList<String> ipv4Result = new ArrayList<String>();
			ArrayList<String> ipv6Result = new ArrayList<String>();
			while (enumeration.hasMoreElements()) {
				final NetworkInterface networkInterface = enumeration.nextElement();
				final Enumeration<InetAddress> en = networkInterface.getInetAddresses();
				while (en.hasMoreElements()) {
					final InetAddress address = en.nextElement();
					if (!address.isLoopbackAddress()) {
						if (address instanceof Inet6Address) {
							ipv6Result.add(normalizeHostAddress(address));
						}
						else {
							ipv4Result.add(normalizeHostAddress(address));
						}
					}
				}
			}

			// 优先使用ipv4
			if (!ipv4Result.isEmpty()) {
				for (String ip : ipv4Result) {
					if (ip.startsWith("127.0") || ip.startsWith("192.168")) {
						continue;
					}

					return ip;
				}
				
				// 取最后一个
				return ipv4Result.get(ipv4Result.size() - 1);
			}
			// 然后使用ipv6
			else if (!ipv6Result.isEmpty()) {
				return ipv6Result.get(0);
			}
			// 然后使用本地ip
			final InetAddress localHost = InetAddress.getLocalHost();
			return normalizeHostAddress(localHost);
		}
		catch (SocketException e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
		catch (UnknownHostException e) {
			throw new IllegalStateException(e.getMessage(), e);
		}
	}
	public static String normalizeHostAddress(final InetAddress localHost) {
		if (localHost instanceof Inet6Address) {
			return "[" + localHost.getHostAddress() + "]";
		}
		else {
			return localHost.getHostAddress();
		}
	}
	
	public static boolean isLinuxOS() {
		return System.getProperty("os.name").toLowerCase().indexOf("linux") != -1;
	}
	public static boolean isWindowOS() {
		return System.getProperty("os.name").toLowerCase().indexOf("win") != -1;
	}
	
	public static String getPid() {
		String name = runtimeMXBean.getName();
		if (name != null && name.length() > 0 && name.indexOf("@") != -1) {
			return name.split("@")[0];
		}
		
		return null;
	}
	
	/**
	 * 谨慎使用该方法，性能低、资源开销大
	 * @return
	 */
	public static String getServerPort() {
		// 从jmx 中获取服务器端口
		String port = getServerPort("HTTP/1.1", "http");
		if (StringUtils.isBlank(port)) {
			port = getServerPort("HTTP/1.1", "https");
		}
		
		if (StringUtils.isBlank(port)) { // 通过操作系统shell获取服务器端口
			Set<String> portSet = new TreeSet<String>();
			if (isLinuxOS()) {
				List<String> result = execCmd(new String[] { "/bin/sh", "-c", "netstat -tnpl | grep " + getPid() + "/java | awk '{print $4}'" });
				for (String line : result) {
					if (line.indexOf(":ffff:127.0.0.1:") != -1) {
						continue;
					}
					if (line.indexOf(":") != -1) {
						portSet.add(line.substring(line.lastIndexOf(":") + 1, line.length()));
					}
				}
			} 
			else if (isWindowOS()) {
				List<String> result = execCmd(new String[] { "cmd", "/C", "netstat -aon|findstr " + getPid() });
				for (String line : result) {
					if (line.indexOf("LISTENING") == -1 || line.indexOf("127.0.0.1:") != -1 || line.indexOf("0.0.0.0:0") == -1) {
						continue;
					}
					portSet.add(line.substring(line.indexOf("0.0.0.0:") + 8, line.indexOf("0.0.0.0:0")).trim());
				}
			}
			
			if (portSet.size() > 0) {
				port = portSet.iterator().next(); // FIRST
			}
		}
		
		return port;
	}
	
	/**
	 * 获取服务器端口（只支持tomcat）
	 * 
	 * @param protocol
	 * @param scheme
	 * @return
	 */
	public static String getServerPort(String protocol, String scheme) { 
		MBeanServer mBeanServer = null;
		if (MBeanServerFactory.findMBeanServer(null).size() > 0) { 
			mBeanServer = (MBeanServer) MBeanServerFactory.findMBeanServer(null).get(0);
		} 
		if (mBeanServer == null) {
			return null;
		}
		
		Set names = null; 
		try {
			names = mBeanServer.queryNames(new ObjectName("Catalina:type=Connector,*"), null); // TODO support tomcat
		} catch (Exception e) { 
			return null;
		}
		
		if (names != null) {
			try {
				Iterator it = names.iterator(); 
				ObjectName oname = null; 
				while (it.hasNext()) { 
					oname = (ObjectName)it.next(); 
					String pvalue = (String) mBeanServer.getAttribute(oname, "protocol"); 
					String svalue = (String)mBeanServer.getAttribute(oname, "scheme"); 
					if (protocol.equalsIgnoreCase(pvalue) && scheme.equalsIgnoreCase(svalue)) { 
						return ((Integer) mBeanServer.getAttribute(oname, "port")).toString(); 
					}
				}
			} catch (Exception e) {
				throw new IllegalStateException(e.getMessage(), e);
			}
		}
		
		return null;
	}
	


	/**
	 * 执行shell
	 * 
	 * @param command
	 * @return
	 */
	public static List<String> execCmd(String[] command) {
		List<String> result = new ArrayList<String>();
		
		Process process = null;
		BufferedReader reader = null;
		try {
			ProcessBuilder processBuilder = new ProcessBuilder(command);
			processBuilder.redirectErrorStream(true);
			process = processBuilder.start();
			
			reader = new BufferedReader(new InputStreamReader(process.getInputStream()));
			String line = null;
			while ((line = reader.readLine()) != null) {
				result.add(line);
			}
			process.waitFor();
		} catch (Exception e) {
			throw new IllegalStateException(e.getMessage(), e);
		} finally {
			ToolsIoUtils.closeQuietly(reader);
			if (process != null) {
				process.destroy();
			} 
		}
		
		return result;
	}
	
	public static void main(String[] args) {
		List<String> list = new ArrayList();
		list.add("(Not all processes could be identified, non-owned process info");
		list.add("will not be shown, you would have to be root to see it all.)");
		list.add(":::8080");
		list.add(":::8019");
		list.add("::ffff:127.0.0.1:8005");
		for (String line : list) {
			if (line.indexOf(":ffff:127.0.0.1:") != -1) {
				continue;
			}
			if (line.indexOf(":") != -1) {
				System.out.println(line.substring(line.lastIndexOf(":") + 1, line.length()));
			}
		}
	}
}